package com.jhdz.system.utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.ExclusiveGateway;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowElementsContainer;
import org.activiti.bpmn.model.FormProperty;
import org.activiti.bpmn.model.Gateway;
import org.activiti.bpmn.model.ParallelGateway;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.bpmn.model.StartEvent;
import org.activiti.bpmn.model.SubProcess;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.TaskServiceImpl;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.interceptor.CommandExecutor;
import org.activiti.engine.task.Task;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.jhdz.system.activiti.JumpAnyWhereCmd;
import com.jhdz.system.api.domain.SysUser;
import com.jhdz.system.config.SystemConfig;
import com.jhdz.system.domain.BudgetFeeList;
import com.jhdz.system.domain.SysUserConst;
import com.jhdz.system.domain.activiti.BudgetChangeStatus;
import com.jhdz.system.domain.activiti.BudgetProcessForm;
import com.jhdz.system.domain.activiti.FlowData;
import com.jhdz.system.domain.activiti.ProcessNode;
import com.jhdz.system.mapper.budget.SysUserConstMapper;
import com.jhdz.system.mapper.budget.activiti.BudgetChangeStatusMapper;
import com.jhdz.system.service.IBudgetFeeListService;
import com.jhdz.system.service.IBudgetProcessFormService;
import com.jhdz.system.service.ISysRoleService;
import com.jhdz.system.service.ISysUserService;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class ProcessUtils {
	
	public final static String FLOW_DATA_KEY = "FLOW_DATA_KEY";
	
	@Autowired
	RepositoryService repositoryService;
	
	 @Autowired
	 private HistoryService historyService;
	 
	 @Autowired
	 RuntimeService runtimeService;
	 
	 @Autowired
	 TaskService taskService;
	 
	 
	 @Autowired
	 IBudgetFeeListService feeListService;
	 
	 @Autowired
	 SystemConfig systemConfig;
	 
	 @Autowired
	 ISysRoleService roleService;
	 
	 @Autowired
     IBudgetProcessFormService processFormService;
    
     @Autowired
     I18nUtils i18nUtils;
    
     @Autowired
     SysUserConstMapper userConstMapper;
    
     @Autowired
     ISysUserService userService;
     
     @Autowired
     BudgetChangeStatusMapper budgetChangeStatusMapper;
     
     public int addBudgetChangeStatus(BudgetChangeStatus eg) {
    	return  budgetChangeStatusMapper.insertBudgetChangeStatus(eg);
     }
     
     /**
      * 查询财务控制所有用户
      * @return
      */
     public List<SysUser> getFiscalCtrlUser(){
    	 SysUser user = new SysUser();
    	 user.setRoleIds(new Long[] {systemConfig.fiscalCtrl});
    	 return userService.selectUserByRoleIds(user);
     }
     
     public List<Long> getDeptManager(Integer roleId,String... dimCodes){
    	 List<SysUserConst>  userConst = userConstMapper.selectByDimCodes(dimCodes);
 		if(userConst==null||userConst.size()==0) {
 			throw new RuntimeException(i18nUtils.getValue("exception.dimCodeNotFindUser",dimCodes));
 		}
 		Set<Long> constuserIds = new HashSet();
 		userConst.forEach(x->{
 			constuserIds.add(x.getUserId());
 		});
 		
 		Long[] roleIds  =new Long[] {Long.valueOf(roleId)};
 		
 		SysUser paramUser = new SysUser();
 		paramUser.setRoleIds(roleIds);
 		
 		List<SysUser> fiscalBoss = userService.selectUserByRoleIds(paramUser);
 		if(fiscalBoss==null||fiscalBoss.size()==0) {
 			throw new RuntimeException(
 					i18nUtils.getValue("exception.roleNotfindUser",new String[] {systemConfig.deptManger.toString()}));
 		}
 		Set<Long> deptUserIds = new HashSet();
 		
 		fiscalBoss.forEach(x->{
 			deptUserIds.add(x.getUserId());
 		});
 		//从两个相等的数组找到相等的数据
 		List<Long> deptIds = new ArrayList<>();
 		
 		constuserIds.forEach(x1->{
 			deptUserIds.forEach(x2->{
 				if(Long.compare(x1, x2)==0) {
 					deptIds.add(x2);
 				}
 			});
 		});
 		//如果选择的成本中心没有配置部门负责人或者部门负责人没有配置
 		if(deptIds.size()==0||Integer.compare(deptIds.size(), dimCodes.length)!=0) {
 			throw new RuntimeException(i18nUtils.getValue("exception.dimCodeNotMatchRoleUser",
 					new String[] {StringUtils.join(dimCodes, ","),systemConfig.deptManger.toString()}));
 		}
 		
 		return deptIds;
     }
    
    /**
     	* 根据选择的成本中心，设置节点部门负责人
     	* @param delegateTask 流程Task
     * @param roleId 角色id
     * @param variName 设置节点变成名称
     * @param dimCodes 成本中心
     */
    public void setNodeUser(DelegateTask delegateTask, Integer roleId,String variName,String... dimCodes) {
    	List<Long> deptIds = getDeptManager(roleId,dimCodes);
		delegateTask.setVariable(variName, deptIds);
    	
    }
    
    /**
     	* 根据指定角色，设置流程节点处理人员
     * @param delegateTask 流程Task
     * @param roleId 角色id
     * @param processUserName 流程变量名称
     */
    public void setNodeUser(DelegateTask delegateTask,Long roleId,String processUserName){
		Long[] roleIds  =new Long[] {roleId};
		
		SysUser paramUser = new SysUser();
		paramUser.setRoleIds(roleIds);
		
		List<SysUser> fiscalBoss = userService.selectUserByRoleIds(paramUser);
		
		if(fiscalBoss==null||fiscalBoss.size()==0) {
			throw new RuntimeException(i18nUtils.getValue("exception.roleNotfindUser",new String[] {roleId.toString()}));
		}
		if(fiscalBoss.size()==1) {
			delegateTask.setVariable(processUserName, fiscalBoss.get(0).getUserId());
		}else {
			List<String> userIds = new ArrayList<>();
			fiscalBoss.forEach(x->{
				userIds.add(x.getUserId().toString());
			});
			delegateTask.setVariable(processUserName, userIds);
		}
		
	}
    
	 
    /**
     	* 根据成本中心类型，获取财务控制用户id
     * @param costType
     * @return
     */
	 public Set<Long> getCtrlUserIdsByCostType(String costType){
		//根据费用类型，查询流程部署id
			BudgetProcessForm entity = new BudgetProcessForm();
			entity.setCostType(costType);
			List<BudgetProcessForm> pfList = processFormService.selectBudgetProcessFormList(entity);
			if(pfList==null||pfList.size()==0) {
				throw new RuntimeException("此费用类型还没有配置流程，请联系管理员");
			}
			Set<Long> ctrlUserIds = new HashSet();
			pfList.forEach(itemP->{
				ctrlUserIds.add(itemP.getUserId());
			});
			
			return ctrlUserIds;
	 }
	 
	 /**
	  * 根据流程表单关系表查询该流程财务控制id
	  * @param entity
	  * @return
	  */
	 public Set<Long> getCtrlUserIdsByEntity(BudgetProcessForm entity){
		 List<BudgetProcessForm> pfList = processFormService.selectBudgetProcessFormList(entity);
			if(pfList==null||pfList.size()==0) {
				throw new RuntimeException("此费用类型还没有配置流程，请联系管理员");
			}
			Set<Long> ctrlUserIds = new HashSet();
			pfList.forEach(itemP->{
				ctrlUserIds.add(itemP.getUserId());
			});
			
			return ctrlUserIds;
	 }
	 
	 
	 public FlowData getFlowData(String taskId) {
		FlowData data = (FlowData) taskService.getVariable(taskId, FLOW_DATA_KEY);
		log.debug("获取到流程数据flowData={}",data);
		return data;
	 }
	 
	public FlowData getFlowDataByPiid(String piid) {
		Task task = taskService.createTaskQuery().orderByTaskCreateTime().desc()
		.limitTaskVariables(1).processInstanceId(piid).singleResult();
		
		FlowData data =  (FlowData) taskService.getVariable(task.getId(), FLOW_DATA_KEY);
		log.debug("根据流程实例id获取到流程数据={}",data);
		return data;
	}
	
	/**
	 * 增加评论
	 * @param userId 当前操作人id
	 * @param taskId 当前节点id
	 * @param processInstanceId 流程实例id
	 * @param comment 评论内容
	 */
	public void addComment(String userId,String taskId,String processInstanceId,String comment) {
		 Authentication.setAuthenticatedUserId(userId);
		 taskService.addComment(taskId, processInstanceId, comment);
	}
	 
	/**
	 * 更新流程状态
	 * @param processId 流程实例id
	 * @param tableId 主键
	 * @param status 流程状态
	 */
	public int updateProcessStatus(String processId,String tableId,Integer status) {
		//发起成功，更新流程状态
		BudgetFeeList entry = new BudgetFeeList();
		entry.setStatus(status);
		entry.setProcessId(processId);
		entry.setId(tableId);
		return feeListService.updateBudgetFeeList(entry);
	}
	 

	/**
	 * 检查当前用户是否有发起流程权限
	 * @param userId  当亲用户id
	 * @return
	 */
	public Boolean checkUserHasStartProcess(String costType,Long userId) {
		Set<Long> ctrlIds = getCtrlUserIdsByCostType(costType);
		
		if(!ctrlIds.contains(userId)) {
			return false;
		}else return true;
	}
	
	/**
	 * 根据流程定义id获取流程实例
	 * @param processDefId
	 * @return
	 */
	 public Process getProcess(String processDefId) {
	        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefId);
	        return bpmnModel.getProcesses().get(0);
	    }
	 
	 /**
	  	* 根据流程实例id获取流程定义id
	  * @param processInstanceId
	  * @return
	  */
	 public String getProcessDefinitionId(String processInstanceId) {
		return  historyService.createHistoricProcessInstanceQuery().
		 processInstanceId(processInstanceId).singleResult().getProcessDefinitionId();
//		return  runtimeService.createProcessInstanceQuery()
//			.processInstanceId(processInstanceId)
//			.singleResult().getProcessDefinitionId();
	 }
	 
	 /**
	  * 根据流程实例id 获取当前流程信息
	  * @param processInstanceId
	  * @return
	  */
	 public Process getProcessByPIid(String processInstanceId) {
		String pdid =  runtimeService.createProcessInstanceQuery()
						.processInstanceId(processInstanceId)
						.singleResult().getProcessDefinitionId();
		return getProcess(pdid);
		
	 }
	 
	 public void getAllTaskInfo(String piid) {
		Process process =  getProcessByPIid(piid);
		List<UserTask> tasks = process.findFlowElementsOfType(UserTask.class);
		
		tasks.forEach(x->{
			//x.setCandidateUsers(candidateUsers);
			
			Task runTask = taskService.createTaskQuery().taskDefinitionKey(x.getId())
										.processInstanceId(piid).singleResult();
			if(runTask!=null) {
				log.info("运行的task as ={}",runTask.getAssignee());
			}
		});
		
	 }
	 
//	 public void testmySql() {
//		 taskService.createNativeTaskQuery().sql("select * from ")
////		 				.parameter(name, value)
////		 				.listPage(firstResult, maxResults)
//	 }
//	 
	 
	 /**
	     * 获取上一环节节点信息
	     * @param currentNodeId 当前节点ID
	     * @param processDefId 流程定义ID
	     * @return ProcessNode
	     */
	    public ProcessNode getPreOneIncomeNode(String currentNodeId, String processDefId) {
	        final List<ProcessNode> preNodes = new ArrayList<>();
	        getIncomeNodesRecur(currentNodeId, processDefId, preNodes, false);
	        for (int i = 0; i < preNodes.size(); i++) {
	            List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery()
	                    .processDefinitionId(processDefId).activityId(preNodes.get(i).getNodeId()).finished().list();
	            if (historicActivityInstances==null||historicActivityInstances.isEmpty()) {
	                preNodes.remove(i);
	                i--;
	            }
	        }
	        if (preNodes==null||preNodes.isEmpty()) {
	            return null;
	        }
	        return preNodes.get(0);
	    }
	 
    public  void getIncomeNodesRecur(String currentNodeId, String processDefId, List<ProcessNode> incomeNodes, boolean isAll) {
        getIncomeNodesRecur(currentNodeId, processDefId, incomeNodes, isAll, true, null);
    }

	 public void getIncomeNodesRecur(String currentNodeId, String processDefId, List<ProcessNode> incomeNodes, boolean isAll, String originalCurrentNodeId) {
	        getIncomeNodesRecur(currentNodeId, processDefId, incomeNodes, isAll, true, originalCurrentNodeId);
	    }
	 
	 public  void getIncomeNodesRecur(String currentNodeId, String processDefId, List<ProcessNode> incomeNodes, boolean isAll, boolean isFirstExec, String originalCurrentNodeId) {
	        Process process = getProcess(processDefId);
	        if (!isFirstExec) {
	            if (currentNodeId.equals(originalCurrentNodeId)) {
	                return;
	            }
	        }
	        FlowElement currentFlowElement = process.getFlowElement(currentNodeId,true);
	        FlowElementsContainer pec = currentFlowElement.getParentContainer();
	        Collection<FlowElement> fls = pec.getFlowElements();
	        
	        List<SequenceFlow> incomingFlows = null;
	        if (currentFlowElement instanceof UserTask) {
	            incomingFlows = ((UserTask) currentFlowElement).getIncomingFlows();
	        } else if (currentFlowElement instanceof Gateway) {
	            incomingFlows = ((Gateway) currentFlowElement).getIncomingFlows();
	        } else if (currentFlowElement instanceof StartEvent) {
	            incomingFlows = ((StartEvent) currentFlowElement).getIncomingFlows();
	        }
	        if (incomingFlows != null && incomingFlows.size() > 0) {
	            incomingFlows.forEach(incomingFlow -> {
//	                String expression = incomingFlow.getConditionExpression();
	                // 出线的上一节点
	                String sourceFlowElementID = incomingFlow.getSourceRef();
	                // 查询上一节点的信息
	                FlowElement preFlowElement = process.getFlowElement(sourceFlowElementID,true);

	                //用户任务
	                if (preFlowElement instanceof UserTask) {
	                    incomeNodes.add(new ProcessNode(preFlowElement.getId(), preFlowElement.getName()));
	                    if (isAll) {
	                        getIncomeNodesRecur(preFlowElement.getId(), processDefId, incomeNodes, true, false, originalCurrentNodeId);
	                    }
	                }
	                
	                if(preFlowElement instanceof SubProcess) {
//	                	FlowElement end = ((SubProcess) preFlowElement).getFlowElements().stream()
//	                	.filter(item -> item instanceof UserTask)
//	                	.reduce((x,y)->y).get();
	                	incomeNodes.add(new ProcessNode(preFlowElement.getId(), preFlowElement.getName()));
	                	log.info("获取最后节点id={},name={}",preFlowElement.getId(),preFlowElement.getName());

	                }
	                if(preFlowElement instanceof StartEvent) {
	                	getIncomeNodesRecur(preFlowElement.getId(), processDefId, incomeNodes, isAll, false, originalCurrentNodeId);
	                }
	                
	                //排他网关
	                else if (preFlowElement instanceof ExclusiveGateway) {
	                    getIncomeNodesRecur(preFlowElement.getId(), processDefId, incomeNodes, isAll, false, originalCurrentNodeId);
	                }
	                //并行网关
	                else if (preFlowElement instanceof ParallelGateway) {
	                    getIncomeNodesRecur(preFlowElement.getId(), processDefId, incomeNodes, isAll, false, originalCurrentNodeId);
	                }
	            });
	        }
	    }
	 
	 public void getAllTaskInfoByPdid(String pdid) {
			Process process =  getProcess(pdid);
			List<UserTask> tasks = process.findFlowElementsOfType(UserTask.class);
			
			
			tasks.forEach(x->{
				//x.setCandidateUsers(candidateUsers);
				log.info("id={},as={},name={},formKey={}",x.getId(),x.getAssignee(),x.getName(),x.getFormKey());
				
				
				List<FormProperty> formPro = x.getFormProperties();
				if(formPro!=null&&formPro.size()>0) {
					formPro.forEach(item->{
						log.info("form数据start");
						log.info("id={},variable={}",item.getId(),item.getVariable());
						log.info("form数据end");
					});
				}
			});
			
		 }
	 
	 
	 /**
	  * 根据流程实例id获取全部节点信息
	  * @param processInstanceId
	  * @return
	  */
	 public Object getAllNodeInfo(String processInstanceId) {
		Process process =  getProcessByPIid(processInstanceId);
		List<FlowElement> allNode = new LinkedList<>();
		
		process.getFlowElements().forEach(x->{
			log.info("id={},name={},class={}",x.getId(),x.getName(),x.getClass().getName());
			if(x instanceof UserTask) {
				allNode.add(x);
			}else if(x instanceof SubProcess) {
				allNode.addAll(((SubProcess) x).getFlowElements().stream()
						.filter(item -> item instanceof UserTask)
						.collect(Collectors.toList()));
			}
			
		});
		log.info("----------------------");
		allNode.forEach(x->{
			log.info("全部id={},name={},class={}",x.getId(),x.getName(),x.getClass().getName());
		});
		return null;
	 }
	 
	 /**
	     * 返回流程的开始节点
	     *
	     * @param flowElements 流程节点集合
	     */
	    private static FlowElement getStartFlowElement(Collection<FlowElement> flowElements) {
	        for (FlowElement flowElement : flowElements) {
	            if (flowElement instanceof StartEvent) {
	                return flowElement;
	            }
	        }
	        return null;
	    }
	    
	    /**
	     * 根据ID查询流程节点对象, 如果是子任务，则返回子任务的开始节点
	     *
	     * @param Id           节点ID
	     * @param flowElements 流程节点集合
	     */
	    public FlowElement getFlowElementById(String Id, Collection<FlowElement> flowElements) {
	        for (FlowElement flowElement : flowElements) {
	            if (flowElement.getId().equals(Id)) {
	                //如果是子任务，则查询出子任务的开始节点
	                if (flowElement instanceof SubProcess) {
	                    return getStartFlowElement(((SubProcess) flowElement).getFlowElements());
	                }
	                return flowElement;
	            }
	            if (flowElement instanceof SubProcess) {
	                FlowElement flowElement1 = getFlowElementById(Id, ((SubProcess) flowElement).getFlowElements());
	                if (flowElement1 != null) {
	                    return flowElement1;
	                }
	            }
	        }
	        return null;
	    }

	 
	 /**
	     * 获取历史流程实例
	     *
	     * @param processInstanceId
	     * @return
	     */
    public HistoricProcessInstance getHistoricProcessInstance(String processInstanceId) {
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        return historicProcessInstance;
    }
    
    /**
     * 流程回退
     * @param taskId 当前操作节点
     * @param comment 回退批注
     * @param userId 当前操作人
     * @param dimCode 回退的成本中心
     */
    public void goBack(String taskId,String comment,String userId,String dimCode) {
    	Optional<Task> optTask = Optional.ofNullable(taskService.createTaskQuery().taskId(taskId).singleResult());
		if(!optTask.isPresent()) {
			throw new RuntimeException("根据"+taskId+"查询不到节点");
		}
		Task curTask = optTask.get();
		String processDefinitionId = curTask.getProcessDefinitionId();
		
		String curActivityId = runtimeService.createExecutionQuery().executionId(curTask.getExecutionId())
				.singleResult().getActivityId();
		//获取上一节点
		ProcessNode pn = getPreOneIncomeNode(curActivityId, processDefinitionId);
		
		log.debug("上一节点{}",pn);
		
		
		if(pn==null) {
			throw new RuntimeException("没有上一节点，无法回退");
		}
		
		if(roleService.countByRoleIdAndUserId(systemConfig.fiscalBoss, Long.valueOf(userId))>0) {
			taskService.setVariable(taskId, "isGoBack", 1);
		}else {
			taskService.setVariable(taskId, "isGoBack", 0);
		}
		if(StringUtils.isNotBlank(dimCode)) {
			taskService.setVariable(taskId, "goBackDimCode", dimCode);
			//设置退回成本中心
			taskService.setVariable(taskId, "dept_manage", getDeptManager(systemConfig.deptManger, dimCode.split(",")));
//			taskService.setVariable(taskId, "", value);
		}
		
		nodeJumpTo(taskId, pn.getNodeId(), userId+"", taskService.getVariables(taskId), comment);
		
		
    }
    
    /**
 	* 节点跳转
	 * @param taskId 当前任务ID
	 * @param targetNodeId 目标节点定义ID
	 * @param assignee 审批用户
	 * @param variables 流转数据
	 * @param comment 备注/意见
	 */
	public void nodeJumpTo(String taskId, String targetNodeId, String assignee, Map<String, Object> variables, String comment) {
		log.info("开始跳转");
	    CommandExecutor commandExecutor = ((TaskServiceImpl) taskService).getCommandExecutor();
	    commandExecutor.execute(new JumpAnyWhereCmd(taskId, targetNodeId, assignee, variables, comment));
	}

}
